// AudioQueue.cpp: implementation of the CAudioQueue class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "AudioQueue.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAudioQueue::CAudioQueue()
{
	m_nType = XAUDIO_QUEUE;
	InitializeCriticalSection( &m_CSQ );
	m_hQueueEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
	m_nMaxQueueSize = DEFAULT_MAXQUEUE_SIZE;
	m_nQueueSize = 0;
	m_pHead = NULL;
	m_pTail = NULL;
	m_bRun = FALSE;
	ZeroMemory( &m_LastCheckTime, sizeof(FILETIME) );
	ZeroMemory( &m_LastCheckTime2, sizeof(FILETIME) );
	m_nAvgInputSize = 0;
	m_nAvgOutputSize = 0;
}
//CAudioQueue::CAudioQueue( int maxsize )
//{
//	m_nType = XAUDIO_QUEUE;
//	InitializeCriticalSection( &m_CSQ );
//	m_hQueueEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
//	m_nMaxQueueSize = maxsize;
//	m_nQueueSize = 0;
//	m_pHead = NULL;
//	m_pTail = NULL;
//	m_bRun = FALSE;
//	ZeroMemory( &m_LastCheckTime, sizeof(FILETIME) );
//	ZeroMemory( &m_LastCheckTime2, sizeof(FILETIME) );
//	m_nAvgInputSize = 0;
//	m_nAvgOutputSize = 0;
//}

CAudioQueue::~CAudioQueue()
{
	// if this is pktpool m_nQueueSize should be 0
//	if( this!=&pktpool )
		Flush( ); 
	DeleteCriticalSection( &m_CSQ );
	CloseHandle( m_hQueueEvent );
}

void CAudioQueue::EnqueueGrow( XAUDIOPACKET *pkt )
{
	EnterCriticalSection( &m_CSQ );
	if( m_pHead==NULL ) // empty
	{
		m_pHead = m_pTail = pkt;
		m_pHead->next = m_pTail->next = NULL;
		m_nQueueSize = 1;
	}
	else
	{
		m_pTail->next = pkt;
		m_pTail = m_pTail->next;
		m_pTail->next = NULL;
		m_nQueueSize++;
	}
	
	CheckTimeInput( pkt );

	LeaveCriticalSection( &m_CSQ );

	SetEvent( m_hQueueEvent );
}

XAUDIOPACKET* CAudioQueue::Enqueue( XAUDIOPACKET *pkt )
{
	XAUDIOPACKET *pop = NULL;
	EnterCriticalSection( &m_CSQ );
	if( m_pHead==NULL ) // empty
	{
		m_pHead = m_pTail = pkt;
		m_pHead->next = m_pTail->next = NULL;
		m_nQueueSize = 1;
	}
	else if( m_nQueueSize>=m_nMaxQueueSize ) // do not encrease queue
	{
		pop = m_pHead;
		m_pHead = m_pHead->next;
		pop->next = NULL;
		m_pTail->next = pkt;
		m_pTail = m_pTail->next;
		m_pTail->next = NULL;
	}
	else
	{
		m_pTail->next = pkt;
		m_pTail = m_pTail->next;
		m_pTail->next = NULL;
		m_nQueueSize++;
	}
	
	CheckTimeInput( pkt );

	LeaveCriticalSection( &m_CSQ );

	SetEvent( m_hQueueEvent );
	return pop;
}

void CAudioQueue::EnqueueEvent( DWORD event, int channel )
{
	SYSTEMTIME stime;
	XAUDIOPACKET *pkt;
	pkt = NULL;
	//pkt = GetXAudioPacket();

	if( !pkt ) return;
	AUDIOPKT_SET_FORMAT( pkt, AUDIO_EVENT, event );
	pkt->channel = channel;
	pkt->length = 0;
	pkt->next = 0;
	GetLocalTime( &stime );
	SystemTimeToFileTime( &stime, &pkt->time );
	if( pkt = Enqueue( pkt ) )
	{
		//ReleaseXAudioPacket( pkt );
	}
}
	

XAUDIOPACKET* CAudioQueue::Dequeue( )
{
	XAUDIOPACKET *pkt;
	EnterCriticalSection( &m_CSQ );
	if( m_nQueueSize==0 )
		pkt = NULL;
	else if( m_nQueueSize==1 )
	{
		pkt = m_pHead;
		m_pHead = m_pTail = NULL;
		m_nQueueSize = 0;
	}
	else
	{
		pkt = m_pHead;
		m_pHead = m_pHead->next;
		m_nQueueSize--;
		pkt->next = NULL;
	}
	ResetEvent( m_hQueueEvent );
	if( pkt ) CheckTimeOutput( pkt );
	LeaveCriticalSection( &m_CSQ );
	return pkt;
}

void CAudioQueue::Flush() // should not be called from pktpool <== infinite loop
{
	XAUDIOPACKET *pkt;
	EnterCriticalSection( &m_CSQ );
	while( m_pHead!=NULL)
	{
		pkt = m_pHead;
		m_pHead = m_pHead->next;
		delete pkt;
		//ReleaseXAudioPacket( pkt );
	}
	m_pTail = NULL;
	m_nQueueSize = 0;
	LeaveCriticalSection( &m_CSQ );
}

int FileTimeDiff( FILETIME *a, FILETIME *b )
{
	LARGE_INTEGER _a;
	LARGE_INTEGER _b;
	__int64 result;
	memcpy( &_a, a, sizeof(LARGE_INTEGER) );
	memcpy( &_b, b, sizeof(LARGE_INTEGER) );
	result = (_a.QuadPart - _b.QuadPart)/10000;
	return (int)result;
}

void CAudioQueue::CheckTimeInput( XAUDIOPACKET *pkt )
{
	if( m_LastCheckTime.dwLowDateTime )
	{
		int timeDiff = FileTimeDiff( &pkt->time, &m_LastCheckTime );
		if( timeDiff<0 ) timeDiff = 0;
		m_nAvgInputSize = ( m_nAvgInputSize + pkt->length )*1000/(1000 + timeDiff);
		memcpy( &m_LastCheckTime, &pkt->time, sizeof(FILETIME) );
	}
	else
	{
		memcpy( &m_LastCheckTime, &pkt->time, sizeof(FILETIME) );
		m_nAvgInputSize = 0;
	}
}

void CAudioQueue::CheckTimeOutput( XAUDIOPACKET *pkt )
{
	if( m_LastCheckTime2.dwLowDateTime )
	{
		int timeDiff = FileTimeDiff( &pkt->time, &m_LastCheckTime2 );
		if( timeDiff<0 ) timeDiff = 0;
		m_nAvgOutputSize = ( m_nAvgOutputSize + pkt->length )*1000/(1000 + timeDiff);
		memcpy( &m_LastCheckTime2, &pkt->time, sizeof(FILETIME) );
	}
	else
	{
		memcpy( &m_LastCheckTime2, &pkt->time, sizeof(FILETIME) );
		m_nAvgOutputSize = 0;
	}
}